home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- title COMZAP.ASM
- ;...................
- ;Author: Edward Nisley
- ;Entered from July 1985 issue of PC Tech Journal
- ;...................
- ;Program to force DOS 2.0 and 2.1 to reload COMMAND.COM
- ; using the file name specified by SET COMSPEC=
- ;Particularly useful with Vdisks and non-bootable
- ; fixed disks.
- ;
- ;The DOS 2.0 and 2.1 SET COMSPEC= command does not work as
- ; documented. DOS will always attempt to reload COMMAND.COM
- ; from the root directory of the disk drive used during
- ; system initialization (the boot disk).
- ;
- ;This utility finds the string A:\COMMAND.COM in DOS
- ; and replaces it with the file name set by SET COMSPEC=
- ;
- ;How it's done:
- ; the BIOS memory-size interrupt determines the
- ; range of RAM searched for the string.
- ;The string A:\COMMAND.COM always starts at an
- ; offset address xxx9, with a binary zero byte at
- ; offset xxx8 just preceding it. These observations
- ; are used to distinguish the true DOS file name from
- ; the string elsewhere (as in the program.COM file
- ; a VDISK or the program as loaded in RAM)
- ;[Or from same in the keyboard buffer under DEBUG.]
- ;Offset 2C in the PSP contains the segment address of
- ; the environment area passed to this program. The
- ; COMSPEC= string is located within the envrionment.
- ;The COMSPEC= string is copied to the DOS string area
- ; and displayed on the screen as it is being copied.
- ;If the COMSPEC= string is too long, your PC is dead!
- ;The string is truncated to fit the largest space
- ; available, which means that it cannot be used to
- ; reload the command interprteer. Power-off time!
- ;
- ;Assumptions:
- ;-DOS 2.0 or DOS 2.1
- ;-Power-on boot drive is "A"
- ;-You have enough RAM in your system that the
- ; string is not in the last segment. The last segment
- ; is not checked, except in 64K systems.
- ;-The SET COMSPEC string is not too long. The maximum
- ; length is one less than the number defined in cspecmax.
- ;
- ;Exit: DOS function 4CH is used to pass a return code
- ; code 0=success!
- ; code 1=DOS file name string wasn't found
- ; code 2=COMSPEC string wasn't found
- ; code 3=COMSPEC string was too long, truncated
- ;
- page
- ;...........................
- ;hocus pocus to start up the assembler
- ; and get the addresses correct for a .COM file
- comseg segment 'codeseg'
- assume cs:comseg,ds:comseg,ss:comseg,es:comseg
- comzap proc
- org 100H
- start: jmp around
- ;.........................
- ;The file name we are trying to find. The leading
- ; 0FFH byte ensures that this file name isn't preceded
- ; by a binary zero. This prevents incorrect matching
- ; when Murphy puts it at the correct offset.
- db 0FFH
- bootID db 'A' ;the normal boot disk
- CCstr db ':\COMMAND.COM' ;last part of string
- CCstr_l equ $-CCstr ;string length
- ;.........................
- ;The COMSPEC= string header in the environment area
- specstr db 'COMSPEC='
- specstr_l equ $-specstr ;string length
- ;.........................
- ;Various & sundry constants
- FN_hexit dw 0009H ;file name offset hexit
- env_seg_at equ 002CH ;address of environment segment
- prt_chr equ 02H ;DOS single-character output
- prt_str equ 09H ;DOS print-string function
- quit equ 4CH ;DOS termination function
- mem_sz equ 12H ;BIOS memory-size interrupt
- dosint equ 21H ;DOS function interrupt
- cr equ 0DH ;useful characters
- lf equ 0Ah
- term equ '$' ;the prt_str terminator
- ;.....................
- ;A macro to simplify the DOS interface
- DOScall macro DOS_fn
- mov ah,DOS_fn
- int dosint
- endm
- page
- ;.......................
- ;Variables
- FN_at label dword ;seg:offset address of
- FN_off dw 0 ;DOS file name string
- FN_seg dw 0
- cspec_at label dword ;seg:offset of COMSPEC=
- cspec_off dw 0 ;string in enfironment
- cspec_seg dw 0
- cspecmax equ 39 ;longest string allowed
- ; includes trailing zero
- last_seg dw 0 ;last segment to check
- ;.......................
- ;Messages
- Msg0 db 'COMZAP: a DOS fixit utility',cr,lf
- db 'Revised 4 April 85',cr,lf
- db cr,lf
- db 'Searching for A:\COMMAND.COM...'
- CrLf db cr,lf,term
- Msg1 db ' >> not found.',cr,lf
- db ' >> Remember that COMZAP.COM can'
- db cr,lf
- db ' >> be run only once per cold boot'
- db cr,lf,term
- Msg2 db cr,lf
- db 'The DOS command interpreter will be'
- db 'reloaded from',cr,lf
- db ' ',term
- Msg3 db 'Searching for COMSPEC= string...'
- db cr,lf,term
- Msg4 db ' >> not found.',cr,lf
- db ' >> Did you use SET COMSPEC=filespec'
- db cr,lf
- db ' >> before running COMZAP.COM?'
- db cr,lf,term
- Msg5 db cr,lf
- db ' >> WARNING: COMSPEC string too long,'
- db cr,lf
- db ' >> DOS will not reload the interpreter'
- db cr,lf,term
- page
- ;........................
- ;Start of the program code
- ;
- ;Show some identification
- around: cld ;set increment mode
- mov dx,offset Msg0 ;show ID message
- DOScall prt_str
- ;.........................
- ;Get storage size and convert to segment address.
- ;Because the last segment may not be full, we will not
- ; test it for the string.
- ;Because we are testing in units of 64K (full segments)
- ; only the high-order hexit of the segment address
- ; is useful in comparisons.
- ;64K PC's (are there any left?) are special-cases.
- int mem_sz ;storage in 1K units
- mov cl,6 ;shift MSB to bit 15
- shl ax,cl
- and ax,0F000H ;isolate 64K seg hexit
- jz oneseg ;handle 64K machines
- sub ax,01000H ;set last segment
- oneseg: mov last_seg,ax
- ;........................
- ;Search for the starting "A" in "A:\COMMAND.COM"
- ;The inner loop uses CMP to check the 4096 offset
- ; addresses ending in the hexit 9 in each segment.
- ;The outer loop verifies inner-loop hits and ticks
- ; the segment addresses at the end of segments
- mov al,bootID ;the test byte
- mov bx,00000H ;first segment address
- mov es,bx ;into segment register
- assume es:nothing ;tell the assembler
- CClp0: mov si,FN_hexit ;get the offset address
- sub si,0010h ;fix for pre-increment
- mov cx,1000H ;64K/16 = # of 9's
- CClp1: add si,0010H ;tick offset pointer
- cmp al,es:[si] ;test the byte
- loopne CClp1 ;loop if not equal
- ;.....................
- ;Inner loop ended:
- ; if "not equal", the entire segment has been examined
- ; without finding a qualifying "A", so go to next seg
- jne nextseg
- ;......................
- ;We've found an "A" at an offset xxx9
- ; if it's preceded by a binary zero, look closer...
- cmp byte ptr es:[si-1],00H
- je testmore ;hmm... looking good!
- page
- ;.......................
- ;The "A" doesn't pass muster, so continue with the
- ; next byte in the segment.
- ;If CX is zero, all bytes have been tested,
- ; so we must step to the next segment
- cmp cx,0
- jne CClp1
- jmp nextseg
- ;......................
- ;An "A" and a binary zero have been located!!!
- ;Time to compare the strings.
- ;Save the current state, just in case it's a miss
- testmore:
- push si ;save current offest
- push es ; ... segment
- push cx ; ... count
- push ax ; ... test byte
- ;.................
- ;Set the segment address to the paragraph just before
- ; the string to avoid overrunning the segment boundary
- ;This is complicated a bit by the restricted nature of
- ; the 8088's "general" registers
- mov bx,si ;get current offset
- mov cl,4 ;convert to paragraphs
- shr bx,cl
- mov cx,es ;add the segment
- add bx,cx
- mov es,bx ;set in in seg reg
- mov FN_seg,bx ;save segment
- ;.....................
- ;Set up the offset addresses to the known string and
- ; the candidate we just found
- ;The candidate offset is always FN_hexit by definition
- mov di,FN_hexit ;known candidate offset
- mov FN_off,di ;save offset
- inc di ;step over "A"
- mov si,offset CCstr ;known string
- mov cx,CCstr_l ; ... its length
- ;....................
- ;After all that, this is the string com0parison.
- ;Registers are restored before the branch simply to
- ; avoid a double branch.
- ;The CMPS operands are coded for human readability.
- ;Failure returns us to the inner test loop above.
- ;Success passes us to the COMSPEC= string eaarch.
- repe cmps byte ptr ds:[si],es:[di]
- pop ax ;restore saved regs
- pop cx
- pop es
- pop si
- jne CClp1 ;no good, continue
- jmp gotfile ;swell, break out
- page
- ;.....................
- ;outer loop ending...
- ;Didn't find the string in this segment
- ;Trick the segment until we've completed the search.
- ;Remember that the RAM in the last segment will not
- ; be examined because the segment may not be filled
- ; with RAM...
- ;A tige more code would make the search complete.
- nextseg:
- mov bx,es ;tick the segment pointer
- add bx,1000H
- mov es,bx
- cmp bx,last_seg ;done?
- jbe CClp0 ;nope, back to loops
- mov dx,offset Msg1 ;present error message
- DOScall prt_str
- mov al,1 ;return code
- DOScall quit
- page
- ;......................
- ;Tah-DAH! Found the DOS file specifier in RAM.
- ;Now we have to look for the COMSPEC string.
- ;Each string in the environment is terminated with a
- ; binary zero. The end of the environment area is
- ; marked by two binary zeros.
- ;Note how the "general" pointer registers aren't...
- gotfile:
- mov dx,offset Msg3 ;say where we are
- DOScall prt_str
- mov si,env_seg_at ;points to env seg ptr
- mov es,[si] ;pick up segment addr
- mov cspec_seg,es ;save seg addr
- findspec:
- mov di,cspec_off ;get offs addr
- cmp byte ptr es:[di],00h ;at end?
- je nospec ;sigh, give up
- mov si,offset specstr ;test string
- mov cx,specstr_l
- repe cmps byte ptr ds:[si],es:[di]
- je gotspec ;kapow!
- mov si,di ;use right "general" reg
- junkit: lods byte ptr es:[si] ;scan for end
- cmp al,00H
- jne junkit
- mov cspec_off,si ;set up pointer
- jmp findspec ;and try again
- ;..........................
- ;Didn't find a COMSPEC= string in the environment.
- ;Return to DOS with an error code.
- nospec:
- mov dx,offset Msg4 ;show a sign
- DOScall prt_str
- mov al,2 ;set return code
- DOScall quit
- page
- ;......................
- ;Found the COMSPEC= string in the enfironment area
- ;ES:DI now points to the start of the file spec string
- ;Need to get source in DS:SI and target in ES:DI
- ;Transfer the COMSPEC= string to the DOS file string
- ; and display it as it is transferred.
- ;The trailing binary zero is transferred as well, to
- ; overwrite any previous file name you may have
- ; manually inserted into the DOS file string while
- ; debugging this program...
- ;The string will be truncated to prevent stamping on
- ; other DOS data following the file name area.
- gotspec:
- mov cspec_off,di ;save offset
- mov dx,offset Msg2 ;say what's happening
- DOScall prt_str
- mov cx,cspecmax ;max length allowed
- les di,FN_at ;DOS string address
- lds si,cspec_at ;COMSPEC string addr
- xfer: lods byte ptr ds:[si] ;COMSPEC char
- stos byte ptr es:[di] ;to DOS char
- cmp al,00H ;hit the end?
- je alldone ;yup, quit
- mov dl,al ;nope, show it
- DOScall prt_chr
- loop xfer ;repeat for count
- ;if fall through, error!
- mov ax,cs ;restore DS
- mov ds,ax
- mov dx,offset Msg5 ;tell them about truncation
- DOScall prt_str
- mov al,3 ;set return code
- DOScall quit
- ;..................
- ;Done with transfer and display, return to DOS
- alldone:
- mov ax,cs ;restore DS
- mov ds,ax
- mov dx,offset crlf ;insert some space
- DOScall prt_str
- mov al,0 ;good return code
- DOScall quit ;and exit
- page
- ;....................
- ;hocus pocus to turn off the assembler
- ; and set up .COM starting address
- comzap endp
- comseg ends
- end start
-
-